package com.stone.serverless.rumtime.spring;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.stone.serverless.sdk.constant.ClassPathConstant;
import com.stone.serverless.sdk.loader.ModuleClassLoader;
import com.stone.serverless.sdk.rumtime.ContainerRuntime;
import com.stone.serverless.web.domain.ServerlessClassRequestMappingInfo;
import lombok.SneakyThrows;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/23 16:57
 * @Version: 1.0.0
 */
public class SpringContainerRuntime extends ContainerRuntime {

    private Object context;

    private List<Object> routerInfo = new ArrayList<>();

    @SneakyThrows
    @Override
    public void initContainer(ModuleClassLoader moduleClassLoader) {

        //System.out.println("初始化spring运行环境");
        String basePackage = moduleClassLoader.getEnvironment("basePackage");
        Boolean lazyRefresh = moduleClassLoader.getEnvironment("spring.lazy.refresh");
        Map<String, Object> environment = moduleClassLoader.getEnvironment("environment");


        Class<?> contextClass = moduleClassLoader.loadClass(ClassPathConstant.ANNOTATION_CONFIG_CONTEXT_CLASS_PATH);

        if (ObjectUtil.isNull(contextClass)) {
            System.out.println("运行环境为spring但是容器内没有spring的包");
            return;
        }

        Object context = ReflectUtil.newInstance(contextClass);
        this.context = context;

        //设置classLoader
        ReflectUtil.invoke(context, "setClassLoader", moduleClassLoader);

        /**
         * 自定义配置文件
         */
        if (ObjectUtil.isNotNull(environment)) {
            this.setEnv(context, environment, moduleClassLoader);
        }

        //设置扫描路径
        Method scan = ReflectUtil.getMethod(context.getClass(), "scan", String[].class);
        if (ObjectUtil.isNotNull(scan)) {
            ReflectUtil.invoke(context, scan, new String[]{basePackage});
        }

        /**
         * 如果是立即加载
         */
        if (lazyRefresh == null || BooleanUtil.isFalse(lazyRefresh)) {
            this.springRefresh();
        }
        //System.out.println("初始化spring运行环境完成");
    }


    @SneakyThrows
    private void setEnv(Object context, Map<String, Object> environment, ModuleClassLoader moduleClassLoader) {
        Object getEnvironment = ReflectUtil.invoke(context, "getEnvironment");
        Object getPropertySources = ReflectUtil.invoke(getEnvironment, "getPropertySources");
        try {
            Class<?> aClass = moduleClassLoader.loadClass(ClassPathConstant.SPRING_MAP_PROPERTIES_SOURCE_CLASS_PATH);
            Object aDefault = ReflectUtil.newInstance(aClass, "default", environment);
            Method method = Arrays.stream(ReflectUtil.getMethods(getPropertySources.getClass(), v -> v.getName().equals("addFirst"))).findAny().orElse(null);
            if (ObjectUtil.isNotNull(method)) {
                ReflectUtil.invoke(getPropertySources, method, aDefault);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Object systemProperties = ReflectUtil.invoke(getPropertySources, "get", "systemProperties");
        if (systemProperties != null) {
            String[] propertyNames = ReflectUtil.invoke(systemProperties, "getPropertyNames");
            Properties properties = new Properties();
            for (String str : propertyNames) {
                if (str.equals("spring.liveBeansView.mbeanDomain")) {
                    continue;
                }
                Object source = ReflectUtil.invoke(systemProperties, "getProperty", str);
                properties.put(str, source);
            }
            Class<?> aClass = moduleClassLoader.loadClass(ClassPathConstant.SPRING_PROPERTIES_SOURCE_CLASS_PATH);

            Object p = ReflectUtil.newInstance(aClass, "systemProperties", properties);
            ReflectUtil.invoke(getPropertySources, "remove", "systemProperties");
            ReflectUtil.invoke(getPropertySources, "addAfter", "default", p);
        }
    }


    @SneakyThrows
    public void springRefresh() {
        /**
         * 如果是spring 环境
         */
        ReflectUtil.invoke(context, "refresh");
    }


    @Override
    public void uninstall() {
        if (ObjectUtil.isNotNull(this.context)) {
            ReflectUtil.invoke(context, "stop");
            ReflectUtil.invoke(context, "close");
        }
        this.context = null;
        this.routerInfo.clear();
        this.routerInfo = null;
    }

    @SneakyThrows
    @Override
    public List<Object> getRouters(ModuleClassLoader moduleClassLoader) {
        if (CollUtil.isNotEmpty(routerInfo)) {
            return routerInfo;
        }
        if (ObjectUtil.isNull(context)) {
            return null;
        }
        //把控制器加载到外部
        Map<String, Object> allControllerMap = new HashMap<>();

        Class<?> aClass = moduleClassLoader.loadClass(ClassPathConstant.SPRING_CONTROLLER_CLASS_PATH);
        if (ObjectUtil.isNotNull(aClass)) {
            Map<String, Object> controller = ReflectUtil.invoke(context, "getBeansWithAnnotation", aClass);
            allControllerMap.putAll(controller);
        }

        aClass = moduleClassLoader.loadClass(ClassPathConstant.SPRING_REST_CONTROLLER_CLASS_PATH);
        if (ObjectUtil.isNotNull(aClass)) {
            Map<String, Object> restController = ReflectUtil.invoke(context, "getBeansWithAnnotation", aClass);
            allControllerMap.putAll(restController);
        }


        allControllerMap.forEach((k, v) -> {
            this.routerInfo.add(v);
        });
        return this.routerInfo;

    }

    @Override
    public void refresh() {
        if (ObjectUtil.isNull(context)) {
            return;
        }
        Boolean isRunning = ReflectUtil.invoke(context, "isRunning");
        if (!isRunning) {
            this.springRefresh();
        }

    }

    @Override
    public Object getInstance(String className, ModuleClassLoader moduleClassLoader) {
        if (null == this.context) {
            return null;
        }
        Class<?> aClass = null;
        try {
            aClass = moduleClassLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
        }
        if (ObjectUtil.isNull(aClass)) {
            return null;
        }
        Object getBean = ReflectUtil.invoke(this.context, "getBean", aClass);
        return getBean;
    }

}
